#include <ds/waitq.h>
#include <arch/cpu.h>
#include <process.h>
#include <sched.h>
#include <log.h>

void prepare_to_wait(waitq_t* queue) {
    lnode_t* wq = &cur_proc->waitq;
    assert(list_empty(wq));
    list_append(&queue->queue, &cur_proc->waitq);
}

void try_wait() {
    lnode_t* wq = &cur_proc->waitq;
    if (list_empty(wq)) {
        return;
    }

    cur_proc->state = PROCESS_WAITING;

    uninterruptible_yield();

    list_remove(wq);
}

void wait_on(waitq_t* queue) {
    cli();

    prepare_to_wait(queue);
    try_wait();

    sti();
}

void wake_one(waitq_t* queue) {
    if (list_empty(&queue->queue)) {
        return;
    }

    lnode_t* wq = queue->queue.next;
    struct process* p = container_of(wq, struct process, waitq);

    assert(p->state == PROCESS_WAITING);
    p->state = PROCESS_READY;
    list_remove(wq);
}

void wake_all(waitq_t* queue) {
    if (list_empty(&queue->queue)) {
        return;
    }

    struct process* pos, * n;
    list_for_each(pos, n, &queue->queue, waitq) {
        if (pos->state == PROCESS_WAITING) {
            pos->state = PROCESS_READY;
        }
        list_remove(&pos->waitq);
    }
}